
package rosedb.control;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Semaphore;





import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;
import riulib.gui.imagelist.ImageListAdapter;
import riulib.gui.imagelist.ImageListListener;
import riulib.gui.imagelist.ReferenzImageList;
import riulib.helpers.ScaleImage;
import rosedb.gui.MainFrame;
import rosedb.model.Logger;
import rosedb.model.Rose;
import rosedb.model.SearchComparators;


/**
 *
 * @author riu
 */
public class Controller {

    private MainFrame gui;
    protected Rose currentRose;
    private Data data;
    private Boolean loadingAllowed = true;
    private Semaphore loadS = new Semaphore(1, true);
    private String search = "";
    private Comparator<Rose> currentComperator = new SearchComparators.AlphabeticRoseComparator();
    public static final int SORT_SPEED = 50;
    public static final String MAINFRAME_NAME = "Rosendatenbank";
    public static final String VERSION = "5.0";


    /**
     * 
     * USERHOME/Library/rose_db/data/
     */
    public static final String DATA_DIR = System.getProperty("user.home")+System.getProperty("file.separator")+"Library"+System.getProperty("file.separator")+"rose_db"+System.getProperty("file.separator")+"data"+System.getProperty("file.separator");
    
    

    public Controller(final MainFrame gui) {
        this.gui = gui;
        new File(DATA_DIR+"roses").mkdirs();
        reloadData();

        ImageListListener<File> oneRose = new ImageListAdapter<File>() {

            @Override
            public void imageSelected(int pos) {
                if(currentRose!=null)
                    autoCorrectImageNumbering(currentRose);
                if(pos>=0 && pos<gui.getOneRoseImageList().count()){
                    gui.getBtnDelImage().setEnabled(true);
                    
                    gui.getPreviewPanel().simplePreview(pos);
                    gui.getPreviewPanel().repaint();
                    
                }else{
                    gui.getBtnDelImage().setEnabled(false);
                }
            }

            @Override
            public void imageAdded(final File i) {
                new Thread(){
                    @Override
                    public void run() {
                        try{
                            if(gui.getOneRoseImageList().count()==1){
                                gui.getRosesImageList().reloadRose(currentRose);
                                gui.getOneRoseImageList().selectImage(0);
                            }
                        }catch(Exception e){}
                    }
                }.start();
            }

            @Override
            public void imageRemoved(File i) {
                
            }

            @Override
            public void imageHovered(int pos) {
                
            }
        };

        ImageListListener<Rose> roses = new ImageListAdapter<Rose>() {

            @Override
            public void imageSelected(int pos) {
                if(pos>=0 && pos<gui.getRosesImageList().count()){
                    loadRose(gui.getRosesImageList().getReferenz(pos));
                    gui.getBtnDelRose().setEnabled(true);
                }else{
                    gui.getBtnDelRose().setEnabled(false);
                }
            }

            @Override
            public void imageAdded(Rose i) {

            }

            @Override
            public void imageRemoved(Rose i) {

            }

            @Override
            public void imageHovered(int pos) {
                if(pos>=0){
                    gui.getRosesImageList().setToolTipText(gui.getRosesImageList().getReferenz(pos).getName());
                }
            }
        };

        gui.getOneRoseImageList().addImageListListener(oneRose);
        gui.getRosesImageList().addReferenceImageListListener(roses);

        loadRoses();
        
    }


    public Comparator<Rose> getCurrentComperator() {
        return currentComperator;
    }


    public void reloadData(){
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(DATA_DIR+"data.dat")));
            data = (Data)ois.readObject();
            ois.close();
        } catch (Exception ex) {
            data = new Data();
            try{
                new File(DATA_DIR).mkdirs();
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(DATA_DIR+"data.dat")));
                oos.writeObject(data);
                oos.close();
            }catch(Exception e){
                Logger.log(e);
            }
            
        }
    }

    public void saveData(){
        try{
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(DATA_DIR+"data.dat")));
            oos.writeObject(data);
            oos.close();
        }catch(Exception e){
            data = new Data();
            try{
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(DATA_DIR+"data.dat")));
            oos.writeObject(data);
            oos.close();
            }catch(Exception e1){
                Logger.log(e);
            }
        }
    }

    public void loadRoses(){
       gui.getRosesImageList().clear();
       gui.getRosesImageList().repaint();
       
       
       File roses = new File(DATA_DIR+"roses");
       if(!roses.mkdirs()){
           for(File f : roses.listFiles(new FileFilter() {

                @Override
                public boolean accept(File pathname) {
                    return pathname!=null && pathname.isDirectory();
                }
            })){
                try {
                    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f.getAbsolutePath()+"/features.rose"));
                    Rose r = (Rose)ois.readObject();
                    
                    if(r.getImageCount()>0){
                        BufferedImage img = ImageIO.read(new File(f.getAbsolutePath()+"/img0.jpg"));
                        ScaleImage.Rectangle preferedSize = ScaleImage.fitToRect(new ScaleImage.Rectangle(0, 0, gui.getRosesImageList().getHeight(), gui.getRosesImageList().getHeight()), img);
                        gui.getRosesImageList().addImage(ScaleImage.scale(img,
                                preferedSize.width, preferedSize.heigth), r.getName(), r);
                    }else{
                        BufferedImage img = ImageIO.read(this.getClass().getResourceAsStream("/rosedb/resources/nopics.jpg"));
                        ScaleImage.Rectangle preferedSize = ScaleImage.fitToRect(new ScaleImage.Rectangle(0, 0, gui.getRosesImageList().getHeight(), gui.getRosesImageList().getHeight()), img);
                        gui.getRosesImageList().addImage(ScaleImage.scale(img,
                                preferedSize.width, preferedSize.heigth), r.getName(), r);
                    }
                    
                } catch (IOException ex) {
                    Logger.log((ex.getMessage()+" in File: "+f.getAbsolutePath()));
                }catch(ClassNotFoundException ce){
                    Logger.log((ce.getMessage()+" with File: "+f.getAbsolutePath()));
                }
           }
       }
       
       System.gc();
    }


    public Controller.Data getData(){
        return data;
    }

    public synchronized void loadRose(final Rose r){    //FIXME Bei Programmstart werden beim ersten auswählen einer Rose nicht alle bilder geladen
        if(r!=null && !r.equals(currentRose)){
            unloadRose();
            currentRose = r;
            autoCorrectImageNumbering(currentRose); //TODO wird kritisch bei vielen Bildern...
            final int c = r.getImageCount();
            
            gui.viewRoseMode();
            
            //Bilder laden... // TODO geschmeidiger machen...
            final Thread t = new Thread(){
                @Override
                public void run(){
                    try{
                        loadingAllowed = true;
//                        
                        gui.getOneRoseImageList().waitTilItIsFuckingBigEnoughToPutPicturesInIt();

                        if(c==0){try{
                                BufferedImage img = ImageIO.read(this.getClass().getResourceAsStream("/rosedb/resources/nopics.jpg"));
                                ScaleImage.Rectangle preferedSize = ScaleImage.fitToRect(new ScaleImage.Rectangle(0, 0, gui.getPreviewPanel().getHeight(), gui.getPreviewPanel().getHeight()), img);
                                gui.getPreviewPanel().simplePreview(ScaleImage.scale(img,
                                preferedSize.width, preferedSize.heigth));
                                gui.getPreviewPanel().repaint();
                                }catch(Exception e){Logger.log(e);}
                        }

                        for(int i=0; i<c; i++){
                            
                            loadS.acquire();
                            if(loadingAllowed==null||!loadingAllowed){
                                i=c;
                            }else{
                                try{
                                    gui.getOneRoseImageList().addImage(new File(DATA_DIR+"roses/"+r.getName().toLowerCase()+"/img"+i+".jpg"));
                                }catch(RuntimeException e){
                                    Logger.log(e);
                                }
                            }
                            loadS.release();
                        }
                        
                        loadingAllowed = null;
                    }catch(InterruptedException e){Logger.log(e);}
                }
            };
            
            t.start();


            gui.getFeaturePanel().loadRose(r);
            
        }
    }


    public boolean renameRose(String newName){
        if(currentRose!=null && newName!=null && !newName.equals("")){
            String name = newName.toLowerCase();
            String oldName = currentRose.getName().toLowerCase();
            if(new File(DATA_DIR+"roses/"+oldName).renameTo(new File(DATA_DIR+"roses/"+name))){
                currentRose.getData().put("name", newName);
                updateRose();
                gui.getRosesImageList().setTitle(gui.getRosesImageList().indexOf(currentRose), newName);
                gui.getRosesImageList().sort(currentComperator, true, SORT_SPEED, true, ReferenzImageList.SortAlgorithm.Quicksort);
                return true;
            }else{
                return false;
            }
        }
        return false;
    }

    public void updateRose(){
        if(currentRose!=null){
            try {
                new File(DATA_DIR+"roses/" + currentRose.getName().toLowerCase()).mkdirs();
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(DATA_DIR+"roses/" + currentRose.getName().toLowerCase() + "/features.rose")));
                oos.writeObject(currentRose);
                oos.close();
            } catch (IOException ex) {
                Logger.log(ex);
            }
            
        }
    }


    public void loadImagesForRose(){
        try{
            loadImagesForRose(currentRose);
        }catch(Exception e){
            Logger.log(e);
        }
    }

    public void loadImagesForRose(Rose rose) throws IOException{
        if(rose!=null && !rose.getName().equals("")){
            JFileChooser chooser = new JFileChooser(data.getLastPath());
            chooser.setMultiSelectionEnabled(true);
            FileNameExtensionFilter filter = new FileNameExtensionFilter(
                "JPG & GIF Bilder", "jpg", "gif", "JPG", "GIF");
            chooser.setFileFilter(filter);
            int returnVal = chooser.showOpenDialog(gui);
            int start = rose.getImageCount();
            if(returnVal == JFileChooser.APPROVE_OPTION) {
                data.setLastPath(chooser.getCurrentDirectory().getAbsolutePath());
                saveData();
                for(int i=0; i<chooser.getSelectedFiles().length; i++){
                    BufferedImage dati = ImageIO.read(chooser.getSelectedFiles()[i]);
                    int px = 1920;
                    int py = 1080;
                    
                    ScaleImage.Rectangle r = ScaleImage.fitToRect(px, py, dati);
                    BufferedImage scaled = (BufferedImage)ScaleImage.scale(dati, r.width, r.heigth);
                    File newPic = new File(DATA_DIR+"roses/"+rose.getName().toLowerCase()+"/img"+(i+start)+".jpg");
                    ImageIO.write(scaled, "jpg", newPic);
                    gui.getOneRoseImageList().addImage(newPic);
                }
            }
        }
    }

    public synchronized void deleteImage(int index){
        if(currentRose!=null && index>=0 && index<gui.getOneRoseImageList().count()){
            try{
                gui.getOneRoseImageList().removeImage(index);

                File f = new File(DATA_DIR+"roses/"+currentRose.getName().toLowerCase()+"/img"+index+".jpg");
                if(f.exists() && f.delete()){
                    autoCorrectImageNumbering(currentRose);
                    if(index == 0){
                        gui.getRosesImageList().reloadRose(currentRose);
                    }
                }

                for(int i=index; i<gui.getOneRoseImageList().count(); i++){
                    gui.getOneRoseImageList().replaceImage(i, new File(DATA_DIR+"roses/"+currentRose.getName().toLowerCase()+"/img"+(i)+".jpg"));
                }

                if(index>0 && gui.getOneRoseImageList().count()>=1){
                    gui.getOneRoseImageList().selectImage(index-1);
                }else if(index==0 && gui.getOneRoseImageList().count()>0){
                    gui.getOneRoseImageList().selectImage(index);
                }else{
                    gui.getOneRoseImageList().selectImage(-1);
                }


                
            }catch(Exception e){
                Logger.log(e);
            }

        }
    }



    public synchronized void unloadRose(){
        try{
            loadS.acquire();
            if(loadingAllowed!=null)
                loadingAllowed = false;
            loadS.release();
        }catch(InterruptedException e){}

        gui.getFeaturePanel().loadRose(null);
        currentRose = null;
        
        gui.noRoseMode();
        gui.getOneRoseImageList().clear();
    }

    public synchronized void deleteRose(){ 
        if(currentRose!=null){
            Rose r = currentRose;
            unloadRose();
            gui.getRosesImageList().removeImage(gui.getRosesImageList().indexOf(r));
            gui.getRosesImageList().selectImage(-1);
            deleteRekursiv(new File(DATA_DIR+"roses/"+r.getName().toLowerCase()));
        }
    }

    private void deleteRekursiv(File f){
        if(f!=null && f.exists()){
            if(f.isDirectory()){
                for(File fi : f.listFiles()){
                    deleteRekursiv(fi);
                }
                f.delete();
            }else{
                f.delete();
            }
        }
    }


    private void autoCorrectImageNumbering(Rose r){
        if(r!=null){
            File roseDir = new File(DATA_DIR+"roses/"+r.getName().toLowerCase());
            if(roseDir.exists() && roseDir.isDirectory()){
                File[] imgs = roseDir.listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File pathname) {
                        return pathname!=null&&pathname.exists()&&pathname.isFile()&&pathname.getName().toLowerCase().endsWith(".jpg");
                    }
                });

                Arrays.sort(imgs, new Comparator<File>(){

                    @Override
                    public int compare(File o1, File o2) {
                        if(!o1.getName().toLowerCase().startsWith("img")){
                            return 1;
                        }else if(!o2.getName().toLowerCase().startsWith("img")){
                            return -1;
                        }else{
                            int z1 = 0;
                            String f1 = o1.getName().toLowerCase().replace("img", "");
                            f1 = f1.replace(".jpg", "");
                            try{z1 = Integer.parseInt(f1);}catch(NumberFormatException e){return 1;}

                            int z2 = 0;
                            String f2 = o2.getName().toLowerCase().replace("img", "");
                            f2 = f2.replace(".jpg", "");
                            try{z2 = Integer.parseInt(f2);}catch(NumberFormatException e){return -1;}

                            return z1-z2;
                        }


                    }
                    
                });

                for(int i=0; i<imgs.length; i++){
                    if(!imgs[i].getName().equals("img"+i+".jpg")){
                        imgs[i].renameTo(new File(DATA_DIR+"roses/"+r.getName().toLowerCase()+"/img"+i+".jpg"));
                    }
                }
            }
        }
    }

    public void updateSearch(String s){
        if(s!=null && !s.equals(search)){
            search = s;

            if(search.equals("")){ // alle zeigen
                currentComperator = new SearchComparators.AlphabeticRoseComparator();
            }
//            else if(search.contains("=")){ // mit bestimmter eigenschaft
//                if(search.contains("&")){ //meherere eigenschaften
//
//                }else{ // nur eine
//
//                }
//            }
            else{// mit bestimmten namen
                currentComperator = new SearchComparators.NameRoseComparator(search);
            }

            gui.getRosesImageList().sort(currentComperator, true, 90, true, ReferenzImageList.SortAlgorithm.Quicksort);
            gui.getRosesImageList().selectImage(-1);
        }
    }

    public Rose getCurrentRose(){
        return currentRose;
    }

    public static class Data implements Serializable{
        private ArrayList<String> features;
        private String lastPath = "";

        public Data() {
            features = new ArrayList<String>();
        }

        public List<String> getFeatures(){
            return features;
        }

        public void setLastPath(String lastPath) {
            this.lastPath = lastPath;
        }

        public String getLastPath() {
            return lastPath;
        }
    }

    



}
